package org.jschlicht.scalaml

import util.parsing.combinator.RegexParsers
import java.lang.IllegalArgumentException
import org.jschlicht.scalaml.ast._
import scala.collection.mutable
/**
 * Parses StandardML sourcecode.
 * @author Jared Schlicht (jschlicht@gmail.com)
 */
class MLParser extends RegexParsers {
  /**
   * Specifies that whitespace and ML-style comments should be skipped.
   */
  override protected val whiteSpace = """(\s|\(\*(\*(?!\))|[^*])*\*\))+""".r

  private val dynEnv = new mutable.Stack[Map[String,Expr]]()
  private var staticEnv = new mutable.Stack[Map[String,Type]]
  
  /* PROGRAMS AND MODULES */
  /**
   * Program
   */
  def program: Parser[Program] = rep1sep(topDec, ";") ^^ {
    topDecs => Program(topDecs)
  }
  
  /**
   * Top level Declaration
   */
  def topDec: Parser[TopDec] = expr
  
  /* DECLARATIONS */
  /**
   * Declaration
   */
  /*
  def dec: Parser[Dec] = valDec
  */
  
  /**
   * Val Declaration
   */
  //def valDec: Parser[ValDec] = pattern <~ "=" ~> expr ^^ {
  //  
  //}
  
  /**
   * Matches an expression in the ML language.
   * This method simply calls the other appropriate parsing methods.
   */
  def expr: Parser[Expr] = realConst | intConst | stringConst
  
  /*
  def infixExpr: Parser[InfixExpr] = rep1(atomicExpr) | infixExpr ~ id ~ infixExpr ^^ {
    case atomicExprs: List[AtomicExpr] => AtomicExprs(atomicExprs)
    case infixExpr ~ id ~ infixExpr => InfixApp(infixExpr, id, infixExpr)
  }
  */
  
  /* MATCHES AND PATTERNS */
  /**
   * Match
   */
  /*
  def patternMatches = rep1sep(pattern <~ "=>" ~> expr, "|") ^^ {
    matches => PatternMatches(matches)
  }
  */
  
  
  /*
  def typedExpr: Parser[Expr] = expr <~ ":" ~> mlType ^^ {
    case expr ~ mlType => TypedExpr(expr, mlType)
  }
  */
  
  /**
   * Parses a real constant.
   * Examples include 0.4, 99.2E23, 4E~3. Integer constants are parsed by
   * {@link #intConst}.
   */
  def realConst: Parser[RealConst] = """(?:~)?\d+(?:(?:\.\d+E(?:~)?\d+)|(?:\.\d+)|(?:E(?:~)?\d+))""".r ^^ { num =>
    /* ML uses a tilda sign to indicate negative values. Scala uses a hyphen.
     * Currently, this method just replaces all tildas with hyphens and lets
     * the toDouble method handle the actual parsing.
     */
    RealConst(num.replace('~', '-').toDouble)
  }

  /**
   * Parses string constants.
   * Examples include "", "Hello", "Test\n123".
   * TODO: String escapes are not currently handled. 
   */
  def stringConst: Parser[StringConst] = "\"" ~> """(?:[^\\"])*""".r <~ "\"" ^^ { s =>
    StringConst(s)
  }
  
  /**
   * Parses an integer constant.
   * Examples include 0, 4, ~223. Real constants are parsed by
   * {@link #realConst}.
   */
  def intConst: Parser[IntConst] = """(?:~)?\d+""".r ^^ { num =>
    IntConst(num.replace('~', '-').toInt)
  }
  
  
  
 
  //def typeVar: Parser[String] = """'(?:')?(?:_)?""".r ~ alphaNumericID
  
  def id: Parser[String] = alphaNumericID | symbolicID
  private val alphaNumericID = """[A-Za-z](?:[A-Za-z0-9'_])*""".r
  private val symbolicID = """(?:[!%&\$#\+\-/:<=>\?@\\~'\|\*])+""".r
  
  

 


  

  
  
  
  /**
   * A helper method to parse expressions. This code will either return the
   * expected result or throw an exception.
   */
  def parseExpr(input: String) = {
    parseAll(expr, input) match {
      case Success(t, _) => t
      case NoSuccess(msg, _) => throw new IllegalArgumentException("Could not parse '" + input + "': " + msg)
    }
  }
}